package com.asa.a;

/**
 * 方法区
 * @author Administrator
 *
 */
public class J {
	
	/**
	 * 
	 * 方法区在哪里?
			《Java虛拟机规范》中明确说明:“尽管所有的方法区在逻辑上是属于堆的一部分，但
			一些简单的实现可能不会选择去进行垃圾收集或者进行压缩。”但对于HotSpotJVM而
			言，方法区还有一个别名叫做Non- Heap (非堆)，目的就是要和堆分开。
			所以，方法区看作是- -块独立于Java堆的内存空间。

	 * 
	 * 
	 * 
	 * 
	 * 方法区的基本理解
			●方法区(Method Area)与Java堆 -样，是各个线程共享的内存区域。
			●方法区在JVM启动的时候被创建，并且它的实际的物理内存空间中和Java堆区--样都
			可以是不连续的。
			● 方法区的大小，跟堆空间一样，可以选择固定大小或者可扩展。.
			●方法区的大小决定了系统可以保存多少个类，如果系统定义了太多的类，导致方法区
			溢出，虚拟机同样会抛出内存溢出错误: java. lang . OutOfMemoryError:
			PermGen space或者j ava. lang . OutOfMemoryError: Metaspace
				加载大量的第三方的jar包; Tomcat部署的工程过多(30-50个)会出现方法区溢出，动态生成的反射类加载太多也会溢出

			●关闭JVM就会释放这个区域的内存。

	 * 
	 * 
	 * Hotspot中方法区的演进
			●在jdk7及以前， 习惯上把方法区，称为永久代。jdk8开始，使用元空间取代了永久代。
			lIn JDK8, dasses metadata is nOw stored in the native heap and this space is clled Metaspace.
			本质上，方法区和永久代并不等价。仅是对hotspot而言的。《Java 虚拟机规范》
			对如何实现方法区，不做统一要求。例如: BEA JRockit/ IBM J9中不存在永久代
			的概念。
			➢现在来看，当年使用永久代，不是好的idea。 导致Java程序更容易OOM (超过-
			XX : MaxPermSize.上限)

	 * 
	 * 
	 * 方法区的大小不必是固定的，jvm可以根据应用的需要动态调整。
			jdk7及以前:
			➢通过-XX: PermSize来设置永久代初始分配空间。默认值是20.75M
			➢-XX:MaxPermsize来设定永久代最大可分配空间。32位机器默认是64M，64位机器模式是82M
			➢当JVM加载的类信息容量超过了这个值，会报异常OutOfMemoryError : Pe rmGen
space

	 * 
	 * 
	 * 	 * jdk8及以后:
			➢元数据区大小可以使用参数-XX :MetaspaceSize和-XX :MaxMetaspaceSize指定,
			替代上述原有的两个参数。
			➢默认值依赖于平台。
			windows'下，-XX:MetaspaceSize是21M， -
			XX :MaxMetaspaceSize的值是-1，即没有限制。
			➢与永久代不同，如果不指定大小，默认情况下，虚拟机会耗尽所有的可用系统内存。
			如果元数据区发生溢出，虚拟机一样会拋出异常OutOfMemoryError: Metaspace
			➢-XX:MetaspaceSize: 设置初始的元空间大小。对于一个64位的服务器端JVM来说, C
			其默认的-XX:MetaspaceSize值为21MB。这就是初始的高水位线，一旦触及这个
			水位线，Full GC将 会被触发并卸载没用的类(即这些类对应的类加载器不再存活)，
			然后这个高水位线将会重置。新的高水位线的值取决于GC后释放了多少元空间。如果
			释放的空间不足，那么在不超过MaxMetaspaceSize时，适当提高该值。如果释放
			空间过多，则适当降低该值。
			➢如果初始化的高水位线设置过低，上述高水位线调整情况会发生很多次。 通过垃圾回
			收器的日志可以观察到Full GC多次调用。为了避免频繁地GC，建议将-XX:MetaspaceSize设置为一个相对较高的值。
	 * 
	 * 要测试让方法区内存溢出还不怎么好办
	 * 	借助CGLib使得方法区溢出，吧方法区设置小一点
	 * 	OOMTest.javaz自定义类加载器，不断的创建类
	 * 
	 * 
	 * 
	 * 
	 * 方法区( Method Area )存储什么?
		《深入理解Java虚拟机》书中对方法区(Method Area)存储内容描述如下:
		它用F存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓
		存等。

	 * 
	 * 
	 * 方法区的内部结构
		域(Fielgl)信息。
		JVM必须在方法区中保存类型的所有域的相关信息以及域的声明顺序。
		域的相关信息包括:
		域名称、域类型、域修饰符(public，private,protected, static, final, volatile, transient的某个子集)

	 * 方法(Method)信息
		JVM必须保存所有方法的以下信息，同域信息一样包括声明顺序:
		●方法名称
		●方法的返回类型(或void)
		方法参数的数量和类型(按顺序)
		●方 法的修饰符(public, private, protected, static, final,
		synchronized, native, abstract的一个 子集)
		●方法的字节码(bytecodes)、操作数栈、局部变量表及大小(abstract和native方法除外)
		●异常表( abstract和native方法除外)
			➢每个异常处理的开始位置、结束位置、代码处理在程序计数器中的偏移地址、
			被捕获的异常类的常量池索引

	 * 
	 * non-final的类变量
			静态变量和类关联在-起，随着类的加载而加载,它们成为类数据在逻辑上的一-部分。
			类变量被类的所有实例共享，即使没有类实例时你也可以访问它。

	 * 补充说明:全局常量: static final
			被声明为final的类变量的处理方法则不同，每个全局常量在编译的时候就
			会被分配了。

	 * 
	 * 
	 * 
	 * 运行时常量池vs常量池
			●方法区，内部包含了运行时常量池。
			●
			字节码文件，内部包含了常量池。
			●要弄清楚方法区，需要理解清楚ClassFile， 因为加载类的信息都在方法区
			●要弄清楚方法区的运行时常量池，需要理解清楚ClassFile中的常量池。如下:
			https:/ /docs. oracle. com/ javase/ specs/ j vms/ se8/html/ jvms-4. html。
			

	 * 
	 * -个有效的字节码文件中除了包含类的版本信息、字段、方法以及接口等描述信息外，还包含一项信息那就是常量池表(Constant Pool Table)，
		包括各种字面量和对类型、域和方法的符号引用。

	 * 
	 * 
	 * 为什么需要常量池?
			一个java源文件中的类、接口，编译后产生一个字节码文件。而Java中的字节码需要数据
			支持，通常这种数据会很大以至于不能直接存到字节码里，换另一种方式，可以存到常量池
			这个字节码包含了指向常量池的引用。在动态链接的时候会用到运行时常量池，之前有介绍
			比如:如下的代码:
			public class simpleClass {
			public void sayHello() {
			System. out. println("hello");
			}
			虽然只有194字节，但是里面却使用了String、System、 PrintStream及
			object等结构。这里代码量其实已经很小了。如果代码多，引用到的结构会更多!
			这里就需要常量池了!

	 * 
	 * 
	 * 
	 * 小结:
		常量池，可以看做是一张表，虚拟机指令根据这张常量表找到要执行的类
		名、方法名、参数类型、字面量等类型。

	 * 
	 * 《有一个整体的，方法区（常量池）和jvm栈联动》
	 * 
	 * 
	 * 
	 * 
	 * 方法区的演迸细节
			1.首先明确:只有HotSpot才有永久代。
			BEA JRockit、 IBM J9等来说，是不存在永久代的概念的。原则上如何实现方
			法区属于虚拟机实现细节，不受《Java虚拟机规范》管束，并不要求统一-。
			2. Hotspot中方法区的变化:
			jdk1.6及之前
				有永久代(permanent generation) ，静态变量存放在永久代上
			jdk1.7
				有永久代，但已经逐步“去永久代”，字符串常量池、静
				态变量移除，保存在堆中

			jdk1.8及之后
				无永久代，类型信息、字段、方法、常量保存在本地内存
				的元空间，但字符串常量池、静态变量仍在堆


	 * 
	 * 
	 * 
	 * 
	 * 
	 * 
	 * 
	 * 
	 * ●
			随着Java8的到来，HotSpot VM中再也见不到永久代了。但是这并不意味着类
			的元数据信息也消失了。这些数据被移到了一个与堆不相连的本地内存区域，这个
			区域叫做元空间( Metaspace )。
			●
			由于类的元数据分配在本地内存中，元空间的最大可分配空间就是系统可用内存空
			间。
			●
			这项改动是很有必要的，原因有:
				1)为永久代设置空间大小是很难确定的。
				在某些场景下，如果动态加载类过多，容易产生Perm区的0OM。比如某个实际Web工
				程中，因为功能点比较多，在运行过程中，要不断动态加载很多类，经常出现致命错误。
				“Exception in thread‘dubbo client x.x connector’java. lang.OutOfMemoryError: PermGen
				space
				而元空间和永久代之间最大的区别在于:元空间并不在虚拟机中，而是使用本地内存。
				因此，默认情况下，元空间的大小仅受本地内存限制。
				2)对永久代进行调优是很困难的。

	 * 
	 * 
	 * 
	 * 有些人认为方法区(如HotSpot虚拟机中的元空间或者永久代)是没有垃圾收集行
		为的，其实不然。《Java虚拟机规范》 对方法区的约束是非常宽松的，提到过可
		以不要求虚拟机在方法区中实现垃圾收集。事实上也确实有未实现或未能完整实
		现方法区类型卸载的收集器存在(如JDK11时期的ZGC收集器就不支持类卸载)。
		一般来说这个区域的回收效果比较难令人满意，尤其是类型的卸载，条件相当苛
		刻。但是这部分区域的回收有时又确实是必要的。以前Sun公司的Bug列表中，曾.
		出现过的若干个严重的Bug就是由于低版本的HotSpot虚拟机对此区域未完全回收
		而导致内存泄漏。
		方法区的垃圾收集主要回收两部分内容:常量池中废弃的常量和不再使用的类型。

	 * 
	 * 
	 * 
	 * 
	 * 
	 * 
	 * 方法区的垃圾收集
			有些人认为方法区(如HotSpot虚拟机中的元空间或者永久代)是没有垃圾收集行为的，其
			实不然。《Java虚拟机规范》 对方法区的约束是非常宽松的，提到过可以不要求虚拟机在方
			法区中实现垃圾收集。事实上也确实有未实现或未能完整实现方法区类型卸载的收集器存在
			(如JDK11时期的ZGC收集器就不支持类卸载)
			9 一般来说这个区域的回收效果比较难令人满意，尤其是类型的卸载，条件相当苛刻。但是这
			部分区域的回收有时又确实是必要的。以前Sun公司的Bug列表中，曾出现过的若千个严重
			的Bug就是由于低版本的HotSpot虚拟机对此区域未完全回收而导致内存泄漏。
			方法区的垃圾收集主要回收两部分内容:常量池中废弃的常量和不再使用的类型。

	 * 
	 * 
	 * 
	 * 先来说说方法区内常量池之中主要存放的两大类常量:字面量和符号引用。
		字面量比较接近Java语言层次的常量概念，如文本字符串、被声明为final
		的常量值等。而符号引用则属于编译原理方面的概念，包括下面三类常量:
				➢1、类和接口的全限定名
				➢2、字段的名称和描述符
				➢3、方法的名称和描述符
		HotSpot虛拟机对常量池的回收策略是很明确的，只要常量池中的常量没有
		被任何地方引用，就可以被回收。
		回收废弃常量与回收Java堆中的对象非常类似。

	 * 常量的回收还好
	 * 
	 * 
	 * 判定一个常量是否“废弃”还是相对简单，而要判定-一个类型是否属于“不再被使用
			的类”的条件就比较苛刻了。需要同时满足下面三个条件:
				➢该类所有的实例都已经被回收，也就是Java堆中不存在该类及其任何派生子类的
				实例。
				➢加载该类的类加载器已经被回收，这个条件除非是经过精心设计的可替换类加载
				器的场景，如OSGi、 JSP的重加载等，否则通常是很难达成的。
				➢该类对应的java.1ang.Class对 象没有在任何地方被引用，无法在任何地方通过
				反射访问该类的方法。
			Java虛拟机被允许对满足上述三个条件的无用类进行回收，这里说的仅仅是“被允
			许”，而并不是和对象一样，没有引用了就必然会回收。关于是否要对类型进行回收，
			HotSpot虚拟机提供了-Xnoclassgc参数进行控制，还可以使用-verbose:class以及
			-XX:+TraceClass-Loading、-XX:+TraceClassUnLoading查 看类加载和卸载信息
			在大量使用反射、动态代理、CGLib等字节码框架，动态生成JSP以及OSGi这类频繁
			自定义类加载器的场景中，通常都需要Java虚拟机具备类型卸载的能力，以保证不会
			对方法区造成过大的内存压力。

	 * 
	 * 
	 * 
	 * 
	 * 
	 * 有些人认为方法区(如HotSpot虚拟机中的元空间或者永久代)是没有垃圾收集行为的，其
		实不然。《Java虛拟机规范》 对方法区的约束是非常宽松的，提到过可以不要求虛拟机在方
		法区中实现垃圾收集。事实上也确实有未实现或未能完整实现方法区类型卸载的收集器存在
		(如JDK 11时期的ZGC收集器就不支持类卸载)。
		'一般来说这个区域的回收效果比较难令人满意，尤其是类型的卸载，条件相当苛刻。但是这
		部分区域的回收有时又确实是必要的。以前Sun公司的Bug列表中，曾出现过的若干个严重
		的Bug就是由于低版本的Hotspot虚拟机对此区域未完全回收而导致内存泄漏。
		方法区的垃圾收集主要回收两部分内容:常量池中废弃的常量和不再使用的类型。

	 * 
	 * 
	 * 
	 * 
	 * 
	 */
	
	
	

}
